package main

import (
	"JVM/Jvmgo/GoToJVM/instructions"
	"JVM/Jvmgo/GoToJVM/instructions/base"
	"JVM/Jvmgo/GoToJVM/runtimeData"
	"fmt"
)

/*
	解释器
*/

// 使用logInst参数控制是否吧指令执行信息打印到控制台
func interpret(thread *runtimeData.Thread, logInst bool) {
	defer catchErr(thread)
	loop(thread, logInst)
}

func catchErr(thread *runtimeData.Thread) {
	if r := recover(); r != nil {
		//fmt.Printf("LocalVars:%v\n", frame.LocalVars())
		//fmt.Printf("OperandStack:%v\n", frame.OperandStack())
		logFrames(thread)
		panic(r)
	}
}

// 打印jvm信息
func logFrames(thread *runtimeData.Thread) {
	for !thread.IsStackEmpty() {
		frame := thread.PopFrame()
		method := frame.Method()
		className := method.Class().Name()
		fmt.Printf(">> pc:%4d %v.%v%v \n",
			frame.NextPC(), className, method.Name(), method.Descriptor())
	}
}

// 将局部变量表和操作数栈内容打印出来
func loop(thread *runtimeData.Thread, logInst bool) {
	reader := &base.BytecodeReader{}

	for {
		// 每次循环拿到当前帧，然后根据pc从当前方法解码出一条指令，判断是否还有帧，有的话继续
		frame := thread.CurrentFrame()
		pc := frame.NextPC()
		thread.SetPC(pc)

		// 一. 解码
		// 1. 设置指针位置
		reader.Reset(frame.Method().Code(), pc)
		opcode := reader.ReadUint8()
		inst := instructions.NewInstruction(opcode)
		inst.FetchOperands(reader)
		frame.SetNextPC(reader.PC())

		if logInst {
			logInstruction(frame, inst)
		}

		// 执行
		inst.Execute(frame)
		if thread.IsStackEmpty() {
			break
		}
	}
}

// 打印指令信息
func logInstruction(frame *runtimeData.Frame, instruction base.Instruction) {
	method := frame.Method()
	className := method.Class().Name()
	methodName := method.Name()
	pc := frame.Thread().PC()
	fmt.Printf("%v.%v() #%2d %T %v\n", className, methodName, pc, instruction, instruction)
}
